################################################################################
# The MIT License (MIT)
#
# Copyright (c) 2014 Kevin Haroldsen
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
################################################################################

from .util import enum, guid_bracket

# object type format for bcd:
# 1 ? X   ????   X
# app imagetype  apptype

# 2   ?   1  ?????
# inherit any

# 2   ?   2  ????  X
# inherit app     apptype

# 2   ?   3  ?????
# inherit device

# 3 ???????
# device

ObjectType = enum(Application=0x1,
                  Inheritable=0x2,
                  Device=0x3)


ObjectImageType = enum(Firmware=0x1,
                       Boot=0x2,
                       Ntldr=0x3,
                       Realmode=0x4)


ObjectAppType = enum(FirmwareMgr=0x1,
                     WinBootMgr=0x2,
                     WinBootLdr=0x3,
                     WinResume=0x4,
                     WinMemTest=0x5,
                     Ntldr=0x6,
                     Setupldr=0x7,
                     BootSect=0x8,
                     Startup=0x9)


ObjectInheritClass = enum(Library=0x1,
                          Application=0x2,
                          Device=0x3)


# known guids matching with the aliases and titles
knownguids = {
    # Application Objects
    '9dea862c-5cdd-4e70-acc1-f32b344d4795': ('bootmgr',
                                             'Windows Boot Manager'),
    'a5a30fa2-3d06-4e9f-b5f4-a01df9d1fcba': ('fwbootmgr',
                                             'Firmware Boot Manager'),
    'b2721d73-1db4-4c62-bf78-c548a880142d': ('memdiag',
                                             'Windows Memory Tester'),
    '147aa509-0358-4473-b83b-d950dda00615': ('resume',                          # Why no alias, Microsoft?
                                             'Windows Resume Application'),
    '466f5a88-0af2-4f76-9038-095b170dc21c': ('ntldr',
                                             'Legacy windows Loader'),
    'fa926493-6f1c-4193-a414-58f0b2456d1e': ('current',
                                             'Current Boot Entry'),
    # {default} is handled custom in the code

    # Inheritable Objects
    '5189b25c-5558-4bf2-bca4-289b11bd29e2': ('badmemory',
                                             'RAM Defects'),
    '6efb52bf-1766-41db-a6b3-0ee5eff72bd7': ('bootloadersettings',
                                             'Boot Loader Settings'),
    '4636856e-540f-4170-a130-a84776f4c654': ('dbgsettings',
                                             'Debug Settings'),
    '0ce4991b-e6b3-4b16-b23c-5e0d9250e5d9': ('emssettings',
                                             'Windows EMS Settings'),
    '7ea2e1ac-2e61-4728-aaa3-896d9d0a9f0e': ('globalsettings',
                                             'Global Settings'),
    '1afa9c49-16ab-4a5c-901b-212802da9460': ('resumeloadersettings',
                                             'Resume Loader Settings'),
    '7ff607e0-4395-11db-b0de-0800200c9a66': ('hypervisorsettings',
                                             'Hypervisor Settings'),
    'ae5534e0-a924-466c-b836-758539a3ee3a': ('ramdiskoptions',
                                             'Ramdisk Options'),
}
knownguids = {guid_bracket(k): (guid_bracket(v[0]), v[1]) for k, v in knownguids.items()}

# for the objects that have no well known guid, but still need a title.
# probably because you can have more than one.
extratitles = {
    (1, 2, 3): 'Windows Boot Loader',
    (1, 1, 3): 'Windows Firmware Boot Loader',
    (1, 1, 0xFFFFF): 'Firmware Application',
    (1, 2, 4): 'Resume from Hibernate'
    }


def alias2guid(alias):
    alias = guid_bracket(alias)
    for k, v in knownguids.items():
        if v[0] == alias:
            return k
    assert False


def create_object_type(object_type, object_type_flags, object_id, test=None):
    objTypeMap = {
        ObjectType.Application: 0x1000_0000,
        ObjectType.Inheritable: 0x2000_0000,
        ObjectType.Device: 0x3000_0000,
    }
    objAppTypeMap = {
        ObjectAppType.FirmwareMgr: 0x0010_0000,
        ObjectAppType.WinBootMgr:  0x0020_0000,
        "FIXME1":                  0x0030_0000,              # should be "LEGACY_LOADER"?
        "FIXME2":                  0x0040_0000,              # should be "REAL_MODE"?
    }
    inheritClassMap = {
        ObjectInheritClass.Library: 0x0010_0000,             # should be "ANY"?
        ObjectInheritClass.Application: 0x0020_0000,
        ObjectInheritClass.Device: 0x0030_0000,
    }

    val = objTypeMap[object_type]

    if object_type == ObjectType.Application:
        val += objAppTypeMap[object_type_flags]
    elif object_type == ObjectType.Inheritable:
        val += inheritClassMap[object_type_flags]
    else:
        assert False

    val += object_id

    if test:
        assert val == test, f'create_object_type failed, was {val}, should be {test}'

    return val


def object_info(type):
    if isinstance(type, str):
        type = int(type, 16)
    return (
        (0xF0000000 & type) >> 28,
        (0x00F00000 & type) >> 20,
        (0x000FFFFF & type)
    )


def object_info_str(type):
    'Create a description based on the object type'
    #print(info)
    if isinstance(type, int):
        info = object_info(type)
    else:
        info = type
        type = info_to_objecttype(info)

    if info in extratitles:
        return extratitles[info]

    if info[0] == ObjectType.Application:
        return '%s-based %s Application (%s)' % \
               (ObjectImageType.reverse_mapping.get(info[1], str(info[1])),
                ObjectAppType.reverse_mapping.get(info[2], str(info[2])),
                hex(type)[2:])
    elif info[0] == ObjectType.Inheritable:
        return 'Inheritable for %s Applications (%s)' % (ObjectAppType, hex(type)[2:])
    elif info[0] == ObjectType.Device:
        if info[1:] == (0, 0):
            return 'Device options'
        return 'Unknown Device (%s)' % hex(type)[2:]


def info_to_objecttype(info):
    return (info[0] << 28 |
            info[1] << 20 |
            info[2])
